package org.zhangsh.nio;

import org.zhangsh.socket.HostInfo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * </p>
 *
 * @author zhangsh
 * @version 1.0.0
 * @since 2019-04-26
 */
public class NioTimerServer {


    private static class SelectorThread implements Runnable{

        private Selector selector = null;

        private volatile boolean stop = false;

        public SelectorThread(Selector selector){
            this.selector = selector;
        }

        @Override
        public void run() {

            // 多路复用线程开始轮询
            while(!stop){

                try {
                    // 轮询有没有就绪状态的channel
                    this.selector.select();

                    // 如果有，将返回该channel的selectKey稽核
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while(iterator.hasNext()){
                        // 取出事件
                        SelectionKey key = iterator.next();
                        iterator.remove();

                        this.handlerInput(key);
                    }

                } catch (IOException e) {

                    try {
                        if(this.selector != null){
                            selector.close();
                        }
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    e.printStackTrace();
                }


            }
        }

        /**
         * 处理事件，针对不同的key，做不同的操作
         * @param key
         */
        private void handlerInput(SelectionKey key) throws IOException {

            // 事件非法，不予处理
            if(!key.isValid()){
                return;
            }

            // 连接事件
            if(key.isAcceptable()){
                // 将事件所在channel取出
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                // 经过TCP三次握手，获取该链接
                SocketChannel clientChannel = serverSocketChannel.accept();
                // 设置非阻塞
                clientChannel.configureBlocking(false);
                // 设置TCP参数
                // 将此channel注册到多路复用器，并将监听read事件
                clientChannel.register(this.selector, SelectionKey.OP_READ);

            }

            // 服务器读事件，也就是客户端有输入
            if(key.isReadable()){
                // 将事件所在channel取出
                SocketChannel channel = (SocketChannel) key.channel();
                // 读取客户端的内容，这里需要用一个缓存区
                ByteBuffer byteBuffer = ByteBuffer.allocate(50);
                byteBuffer.clear();
                channel.read(byteBuffer);
                byteBuffer.flip();
                // 回显
                byte[] target = new byte[byteBuffer.remaining()];
                byteBuffer.get(target);
                byteBuffer.clear();
                String clientStr = new String(target, "GBK");
                System.out.println("客户端的输入是:" + clientStr);

                String response = new String("hello I am Server");
                // 回写客户端东西
                this.doWrite(channel, response);
            }
        }

        private void doWrite(SocketChannel channel, String response) throws IOException {

            ByteBuffer byteBuffer = ByteBuffer.allocate(50);
            byteBuffer.put(response.getBytes());
            byteBuffer.flip();
            channel.write(byteBuffer);
        }
    }

    public static void main(String[] args) throws IOException {

        // 首先开始服务channel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 配置非阻塞
        serverSocketChannel.configureBlocking(false);
        // 绑定TCP参数
        serverSocketChannel.socket().bind(new InetSocketAddress(HostInfo.HOST_PORT));

        // 创建多路复用器
        Selector selector = Selector.open();

        // 将ServerSocketChannel注册到多路复用器，并监听AACEPT事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 开以线程，多路复用器开始轮询
        new Thread(new SelectorThread(selector)).start();





    }
}
